#ifndef __BLOCK_MATRIX_H__
#define __BLOCK_MATRIX_H__

#include "block_predeclare.h"
#include "block_vector.h"
#include "block_math.h"


template<typename T>
class Matrix4_T
{
public:
    typedef T                   value_type;

    typedef value_type*         pointer;
    typedef value_type const *  const_pointer;

    typedef value_type&         reference;
    typedef value_type const &  const_reference;

    typedef value_type*         iterator;
    typedef value_type const *  const_iterator;

    enum { row_num = 4, col_num = 4};
    enum { elem_num = row_num * col_num };

private:
    Vector_T<Vector_T<T, col_num>, row_num> m_;

public:
    Matrix4_T()
    {
    }

    explicit Matrix4_T(T const * rhs)
    {
        for (size_t i = 0; i < row_num; ++i)
        {
            m_[i] = Vector_T<T, col_num>(rhs);
            rhs += col_num;
        }
    }

    Matrix4_T(Matrix4_T const & rhs) : m_(rhs.m_) {}

    Matrix4_T(T f11, T f12, T f13, T f14,
              T f21, T f22, T f23, T f24,
              T f31, T f32, T f33, T f34,
              T f41, T f42, T f43, T f44)
    {
        m_[0][0] = f11; m_[0][1] = f12; m_[0][2] = f13; m_[0][3] = f14;
        m_[1][0] = f21; m_[1][1] = f22; m_[1][2] = f23; m_[1][3] = f24;
        m_[2][0] = f31; m_[2][1] = f32; m_[2][2] = f33; m_[2][3] = f34;
        m_[3][0] = f41; m_[3][1] = f42; m_[3][2] = f43; m_[3][3] = f44;
    }

    static size_t size() { return elem_num; }

    static Matrix4_T const & zero()
    {
        static Matrix4_T const z(
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0);
        return z;
    }

    static Matrix4_T const & identity()
    {
        static Matrix4_T const i(
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1);
        return i;
    }

    reference operator()(size_t row, size_t col)
    {
        return m_[row][col];
    }

    const_reference operator()(size_t row, size_t col) const
    {
        return m_[row][col];
    }

    iterator begin()
    {
        return &m_[0][0];
    }

    const_iterator begin() const
    {
        return &m_[0][0];
    }

    iterator end()
    {
        return this->begin() + elem_num;
    }

    const_iterator end() const
    {
        return this->begin() + elem_num;
    }

    reference operator[](size_t index)
    {
        return *(this->begin() + index);
    }

    const_reference operator[](size_t index) const
    {
        return *(this->begin() + index);
    }

    void Row(size_t index, Vector_T<T, col_num> const & rhs)
    {
        m_[index] = rhs;
    }

    Vector_T<T, col_num> const & Row(size_t index) const
    {
        return m_[index];
    }

    void Col(size_t index, Vector_T<T, row_num> const & rhs)
    {
        for (size_t i = 0; i < row_num; ++i)
        {
            m_[i][index] = rhs[i];
        }
    }

    Vector_T<T, row_num> Col(size_t index) const
    {
        Vector_T<T, row_num> ret;
        for (size_t i = 0; i < row_num; ++i)
        {
            ret[i] = m_[i][index];
        }

        return ret;
    }

    Matrix4_T& operator+=(Matrix4_T const & rhs)
    {
        m_ += rhs.m_;
        return *this;
    }

    friend Matrix4_T operator+(Matrix4_T const & lhs, Matrix4_T const & rhs)
    {
        Matrix4_T nrv(lhs); nrv += rhs; return nrv;
    }

    Matrix4_T& operator-=(Matrix4_T const & rhs)
    {
        m_ -= rhs.m_;
        return *this;
    }

    friend Matrix4_T operator-(Matrix4_T const & lhs, Matrix4_T const & rhs)
    {
        Matrix4_T nrv(lhs); nrv -= rhs; return nrv;
    }

    Matrix4_T& operator*=(Matrix4_T const & rhs)
    {
        *this = mul(*this, rhs);
        return *this;
    }

    friend Matrix4_T operator*(Matrix4_T const & lhs, Matrix4_T const & rhs)
    {
        return mul(lhs, rhs);
    }

    Matrix4_T& operator*=(T const & rhs)
    {
        for (size_t i = 0; i < row_num; ++i)
        {
            m_[i] *= rhs;
        }
        return *this;
    }

    friend Matrix4_T operator*(Matrix4_T const & lhs, T const & rhs )
    {
        Matrix4_T nrv(lhs); nrv *= rhs; return nrv;
    }

    friend Matrix4_T operator*(T const & lhs, Matrix4_T const & rhs)
    {
        Matrix4_T nrv(rhs); nrv *= lhs; return nrv;
    }

    Matrix4_T& operator/=(T const & rhs)
    {
        return this->operator*=(1 / rhs);
    }

    friend Matrix4_T operator/(Matrix4_T const & lhs, T const & rhs)
    {
        Matrix4_T nrv(lhs); nrv /= rhs; return nrv;
    }

    Matrix4_T& operator=(Matrix4_T const & rhs)
    {
        if (this != &rhs)
        {
            m_ = rhs.m_;
        }
        return *this;
    }

    Matrix4_T const operator+() const
    {
        return *this;
    }
    Matrix4_T const operator-() const
    {
        Matrix4_T tmp(*this);
        tmp.m_ = -m_;
        return tmp;
    }

    bool operator==(Matrix4_T const & rhs) const
    {
        return m_ == rhs.m_;
    }

};


#endif
